#include <iostream>
#include <cassert>
#include <thread>
#include <vector>
#include <cstring>

#include "SocketOps.hpp"

#ifdef _WIN32
#include "wepoll.h"
#endif

#ifdef __linux__
#include <sys/epoll.h>
#endif


#define SERVER_HOST "0.0.0.0"
#define SERVER_PORT 9091
#define BACKLOG 64
#define MAX_EVENTS 1024
#define BUFFER_SIZE 1024


static char resp[] = 
    "HTTP/1.1 200 OK\r\n"
    "Content-Length: 4\r\n"
    "Content-Type: text/plain\r\n"
    "\r\n"
    "pong";


static void handleAccept(HANDLE epollfd, socket_type listenfd) {
    in_addr_type addr;
    socklen_t addrlen = sizeof(addr);
    
    socket_type sockfd = socket_ops::accept(listenfd, (socket_addr_type*)&addr, &addrlen);

    if(sockfd == invalid_socket) {
        return;
    }

    socket_ops::setnonblocking(sockfd);

    epoll_event event;
    memset(&event, 0, sizeof(event));
    event.events = EPOLLIN;
    event.data.fd = sockfd;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event);
}

static void handleRead(HANDLE epollfd, socket_type sockfd) {
    char buf[BUFFER_SIZE];
    int ret = socket_ops::recv(sockfd, buf, sizeof(buf), 0);
    if(ret <= 0) {
        epoll_ctl(epollfd, EPOLL_CTL_DEL, sockfd, NULL);
        socket_ops::close(sockfd);
        return;
    }
    socket_ops::send(sockfd, resp, strlen(resp), 0);
}


void loopThread(HANDLE epollfd, socket_type listenfd) {
    epoll_event events[MAX_EVENTS];
    memset(events, 0, sizeof(events));
    for(;;) {
        int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if(nfds == -1) {
            perror("epoll_wait failed");
            continue;
        }
        for(int i = 0; i < nfds; i++) {
            if(events[i].data.fd == listenfd) {
                handleAccept(epollfd, listenfd);
            } else {
                handleRead(epollfd, events[i].data.fd);
            }
        }
    }
}

void startLoop(socket_type listenfd) {
    HANDLE epollfd = epoll_create(MAX_EVENTS);
    assert(epollfd >= 0);

    epoll_event event;
    memset(&event, 0, sizeof(event));

    event.events = EPOLLIN;
    event.data.fd = listenfd;
    int ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);
    assert(ret != -1);

    loopThread(epollfd, listenfd);
}


int main() {
#ifdef _WIN32
    WSADATA wsa;
    assert(WSAStartup(MAKEWORD(2,2), &wsa) == 0);
#endif

    int ret;
    socket_type listenSock = socket_ops::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    assert(listenSock != invalid_socket);
    socket_ops::setnonblocking(listenSock);

    in_addr_type sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SERVER_PORT);
    sin.sin_addr.s_addr = inet_addr(SERVER_HOST);
    ret = socket_ops::bind(listenSock, (const socket_addr_type*)&sin, sizeof(sin));
    assert(ret != SOCKET_ERROR);

    ret = socket_ops::listen(listenSock, BACKLOG);
    assert(ret != SOCKET_ERROR);

    std::vector<std::thread> threads;

    std::cout << "loop thread111: " << std::thread::hardware_concurrency() << std::endl;

    for(int i = 0; i < 2; i++) {
        std::thread t(startLoop, listenSock);
        threads.push_back(std::move(t));
    }

    for(int i = 0; i < threads.size(); i++) {
        threads[i].join();
    }

#ifdef _WIN32
    WSACleanup();
#endif
    return 0;
}
